<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Controller;

use App\Service\GfunService;
use Swoole\Client;
use App\Service\MachineService;
use Hyperf\Di\Annotation\Inject;

class MachineController extends AbstractController
{
    #[Inject]
    private MachineService $machineService;



    public function setCallBackUrl()
    {
        $macnostr = $this->request->input('macno');
        $url = $this->request->input('url');
        $record_url = $this->request->input('record_url');
        $sign = $this->request->input('sign');

        if(!empty($macnostr) && !empty($url)){
            if(!empty($url)){
                $macnoarr =explode(",",$macnostr);
                foreach($macnoarr as $macno ) {
                    self::setRedis($macno.":sys_url" , json_encode(array("url" => $url, 'sign' => $sign)));
                }
            }
            if(!empty($record_url)){
                $macnoarr =explode(",",$macnostr);
                foreach($macnoarr as $macno ) {
                    self::setRedis($macno.":record_url", json_encode(array("url" => $record_url, 'sign' => $sign)));
                }
            }
            return $this->success('回调地址设置成功');
        }else{
            return $this->error('设置失败，缺少参数');
        }
    }

    /**
     * 发送签到指令
     * @param string $stationId 充电站编号
     * @param array $data 数据包
     * @return array
     */
    public function sendSignIn()
    {
        $stationId = $this->request->input('stationId');
        $data = $this->request->input('data');
        if(empty($stationId)) return $this->error('no stationId');

        return $this->machineService->sendSignIn($stationId, $data);
    }

    /**
     * 查询余额
     * @param string $stationId 充电站编号
     * @param string $cardNumber 卡号
     * @return array
     */
    public function queryBalance()
    {
        $stationId = $this->request->input('stationId');
        $cardNumber = $this->request->input('cardNumber');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($cardNumber)) return $this->error('no cardNumber');
        return $this->machineService->queryBalance($stationId, $cardNumber);
    }

    /**
     * 刷打开插座上报
     * @param string $stationId 充电站编号
     * @param int $socketId 插座编号
     * @param string $cardNumber 卡号
     * @return array
     */
    public function openSocket()
    {
        $stationId = $this->request->input('stationId');
        $socketId = $this->request->input('socketId');
        $cardNumber = $this->request->input('cardNumber');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socketId)) return $this->error('no socketId');
        if(empty($cardNumber)) return $this->error('no cardNumber');
        return $this->machineService->openSocket($stationId,$socketId, $cardNumber);
    }

    public function ipset()
    {
        $stationId = $this->request->input('stationId');
        if(empty($stationId)) return $this->error('no stationId');
        return $this->machineService->ipset($stationId);
    }

    /**
     * 充电关闭上报
     * @param string $stationId 充电站编号
     * @param int $socketId 插座编号
     * @return array
     */
    public function closeSocket()
    {
        $stationId = $this->request->input('stationId');
        $socketId = $this->request->input('socketId');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socketId)) return $this->error('no socketId');
        return $this->machineService->closeSocket($stationId,$socketId);
    }

    /**
     * 发送心跳
     * @param string $stationId 充电站编号
     * @return array
     */
    public function sendHeartbeat()
    {
        $stationId = $this->request->input('stationId');
        if(empty($stationId)) return $this->error('no stationId');
        return $this->machineService->sendHeartbeat($stationId);
    }

    /**
     * 开始充电
     * @param string $stationId 充电站编号
     * @param int $socketId 插座编号
     * @param int $duration 充电时长（分钟）
     * @return array
     */
    public function startCharge()
    {
        $stationId = $this->request->input('stationId');
        $socketId = $this->request->input('socketId');
        $duration = $this->request->input('duration',0);
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socketId)) return $this->error('no socketId');
        return $this->machineService->startCharge($stationId,$socketId,$duration);
    }

    /**
     * 停止充电
     * @param string $stationId 充电站编号
     * @param int $socketId 插座编号
     * @return array
     */
    public function stopCharge()
    {
        $stationId = $this->request->input('stationId');
        $socketId = $this->request->input('socketId');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socketId)) return $this->error('no socketId');
        return $this->machineService->stopCharge($stationId,$socketId);
    }

    /**
     * 停止过程上报
     * @param string $stationId 充电站编号
     * @param int $socketId 插座编号
     * @return array
     */
    public function reportCharge()
    {
        $stationId = $this->request->input('stationId');
        $socketId = $this->request->input('socketId');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socketId)) return $this->error('no socketId');
        return $this->machineService->reportCharge($stationId,$socketId);
    }

    /**
     * 重启充电站
     * @param string $stationId 充电站编号
     * @return array
     */
    public function rebootStation()
    {
        $stationId = $this->request->input('stationId');
        if(empty($stationId)) return $this->error('no stationId');
        return $this->machineService->rebootStation($stationId);
    }

    /**
     * 重启充电站
     * @param string $stationId 充电站编号
     * @return array
     */
    public function setStation()
    {
        $stationId = $this->request->input('stationId');
        $socket = $this->request->input('socket');
        if(empty($stationId)) return $this->error('no stationId');
        if(empty($socket)) return $this->error('no socket');
        return $this->machineService->setStation($stationId,$socket);
    }



    /**
     * 升级充电站固件
     * @param string $stationId 充电站编号
     * @param string $firmwareVersion 固件版本号
     * @return array
     */
    public function upgradeFirmware()
    {
        $stationId = $this->request->input('stationId');
        $firmwareVersion = $this->request->input('firmwareVersion');
        if(empty($stationId)) return $this->error('no stationId');
        return $this->machineService->upgradeFirmware($stationId,$firmwareVersion);
    }

    /**
     * 构建命令
     * @param string $stationId 充电站编号
     * @param string $cmd 指令码
     * @param array $data 数据包
     * @return string
     */
    private function buildCommand($stationId, $cmd, $data,$zhen = '00',$err = '00')
    {
        $frameHeader = self::FRAME_HEADER;
        $frameTail = self::FRAME_TAIL;

        // 构建数据包
        $dataPacket = $this->buildDataPacket($data);

        // 计算数据长度
        $dataLength = (strlen($dataPacket) / 2) + (strlen($err) / 2); // 数据长度为字节数
        $len = str_pad((string)$dataLength, 2, '0', STR_PAD_LEFT);


        // 构建命令
        $command = $frameHeader . $stationId . $cmd .$zhen. $len .$err. $dataPacket;

        // 计算校验码
        $checksum = $this->calculateChecksum($command);

        // 完整命令
        $fullCommand = $command . $checksum . $frameTail;
        $this->logger->info($fullCommand);
        return $fullCommand;
    }

    /**
     * 构建数据包
     * @param array $data 数据
     * @return string
     */
    private function buildDataPacket($data)
    {
        $packet = '';
        foreach ($data as $key => $value) {
            $packet .= str_pad((string)$value, 2, '0', STR_PAD_LEFT);
        }
        return $packet;
    }

    /**
     * 计算校验码
     * @param string $command 命令
     * @return string
     */
    private function calculateChecksum($command)
    {
//        // 这里使用 CRC16 校验算法
//        $crc = crc16($command);
//        return str_pad((string)dechex($crc), 4, '0', STR_PAD_LEFT);
        return '0000';
    }

    /**
     * 发送命令
     * @param string $stationId 充电站编号
     * @param string $command 命令
     * @return array
     */
    private function sendCommand($stationId, $command)
    {
        $macnoData = self::getRedis($stationId  . ":COM");
        if(empty($macnoData)){
            return $this->parseResponse(['result'=>1]);
        }
        $macnoData = json_decode((string)$macnoData,true);
        $fd = !empty($macnoData['port'])?$macnoData['port']:0;
        if(empty($fd)){
            return $this->parseResponse(['result'=>2]);
        }
        server()->send($fd,hex2bin($command));

        return $this->parseResponse(['result'=>0]);
    }

    /**
     * 解析响应
     * @param string $response 响应数据
     * @return array
     */
    private function parseResponse($response,$code = 1,$msg = '成功')
    {
        // 解析响应数据
        $data = [
            'code' => $code,
            'msg' => $msg,
            'data' => $response,
        ];
        return $data;
    }

    public function error($msg = '请求失败',$data = [],$code = 0){
        // 解析响应数据
        $data = [
            'code' => $code,
            'msg' => $msg,
            'data' => $data,
        ];
        return $data;
    }

    public function success($msg = '请求成功',$data = [],$code = 1){
        // 解析响应数据
        $data = [
            'code' => $code,
            'msg' => $msg,
            'data' => $data,
        ];
        return $data;
    }



    /**
     * 设置Redis数据
     * @param string $key Redis键
     * @param string $value Redis值
     */
    private static function setRedis($key, $value)
    {
        redis()->set($key,$value);
    }



    /**
     * 设置Redis数据
     */
    private static function getRedis($key)
    {
        return redis()->get($key);
    }



}
